# Created by Gao Song on 2019-04-09

from .base_fucker import BaseFucker
from gateway.base_gateway import BaseGateway
from event import Event

from .base_fucker import FuckerStatus
from core.constant import Status as OrderStatus
from datetime import timedelta, datetime, time

import logging
logger = logging.getLogger('fucker')


class TwapFucker(BaseFucker):
    def __init__(self, gateway: BaseGateway, **kwargs):
        super().__init__(gateway)
        self.start_time = kwargs.get('start_time', datetime.now())
        self.end_time = kwargs.get('end_time', datetime.now() + timedelta(minutes=10))
        self.time_slice = kwargs.get('time_slice', 3)
        self.cancel_wait_time = kwargs.get('cancel_wait_time', 10)
        self.max_order_vol = kwargs.get('max_order_vol', 168)

    def on_tick(self, e: Event):
        tick = e.data

        if self.status != FuckerStatus.RUNNING:
            return

        tick_time = tick.datetime.time()

        if tick_time < time(9, 0) or time(16, 0) < tick_time < time(21, 0):
            return

        vt_symbol = tick.vt_symbol

        if self.finished[vt_symbol]:
            return

        now = datetime.now()

        if now < self.start_time:
            return

        if self.change_pos[vt_symbol] > 0:
            if 1e10 > tick.bid_price_1 > 0:
                price = tick.bid_price_1
                if abs(price - tick.low_price) < 1e-10 and abs(price - tick.high_price) > 1e-10:
                    logger.debug(f'{vt_symbol} try to long at low')
                    return
            else:
                return
        else:
            if 1e10 > tick.ask_price_1 > 0:
                price = tick.ask_price_1
                if abs(price - tick.high_price) < 1e-10 and abs(price - tick.low_price) > 1e-10:
                    logger.debug(f'{vt_symbol} try to short at high')
                    return
            else:
                return
        self.check_cancel_orders(vt_symbol, price)

        target_pos = self.target_pos.get(vt_symbol, 0)
        current_pos = self.position_management.get_position_holding(vt_symbol).current_pos

        if target_pos * current_pos <= -1:
            target_pos = 0

        pos_dif = target_pos - current_pos

        trading_pos = self.position_management.get_position_holding(vt_symbol).trading_pos
        logger.debug(f'symbol: {vt_symbol}, target: {target_pos}, current: {current_pos}, trading: {trading_pos}')

        if pos_dif == 0:
            if trading_pos != 0:
                logger.warning('target == current but trading != 0')
            return

        to_trade = pos_dif - trading_pos
        if (self.change_pos[vt_symbol] < 0 and to_trade < 0) or (self.change_pos[vt_symbol] > 0 and to_trade > 0):

            to_trade = self.max_order_vol_cons(vt_symbol, to_trade, now)
            to_trade = self.min_order_vol_cons(vt_symbol, to_trade, now)
            if to_trade != 0:
                self.send_net_pos_order(vt_symbol, price, to_trade)

    def max_order_vol_cons(self, vt_symbol, vol, now):
        all_to_trade = self.change_pos[vt_symbol]
        left_time = int((self.end_time - now).total_seconds() / self.time_slice) * self.time_slice

        if left_time < 0 or self.end_time <= self.start_time:
            return vol

        left_pct = left_time / (self.end_time - self.start_time).total_seconds()

        no_trade = int(all_to_trade * left_pct)

        if vol > 0:
            if vol <= no_trade:
                return 0
            else:
                return vol - no_trade
        else:
            if vol >= no_trade:
                return 0
            else:
                return vol - no_trade

    def get_min_vol(self, vt_symbol):
        if vt_symbol.startswith('CJ'):
            return 4
        return 1

    def min_order_vol_cons(self, vt_symbol, vol, now):
        min_vol = self.get_min_vol(vt_symbol)
        if abs(vol) < min_vol:
            return 0
        return vol

    def check_cancel_orders(self, vt_symbol, price):
        for order in self.position_management.get_position_holding(vt_symbol).active_orders.values():
            if abs(order.price - price) >= 0.1:
                if order.status != OrderStatus.INIT:
                    now = datetime.now()
                    ot = datetime.strptime(order.time, '%H:%M:%S')
                    ot = ot.replace(year=now.year, month=now.month, day=now.day)
                    if (now - ot).total_seconds() > self.cancel_wait_time:
                        self.cancel_order(order)
